package com.h2.gwt.util.net;

import java.util.ArrayList;
import java.util.List;

import com.h2.gwt.util.net.HttpUtils.Protocol;

public class UrlBuilder {

   private Protocol _protocol;
   
   private String _host;
   
   private Integer _port;
   
   private String _baseUrl;
   
   private String _postfixUrl;
   
   /** See if port 80 should be added if present or null. */
   private Boolean _addPort80 = Boolean.FALSE;
   
   private List<NameValuePair<?>> _parameters =
      new ArrayList<NameValuePair<?>>();
   
   ////
   ////

   public UrlBuilder() {
      //do nothing
   }
   
   public UrlBuilder(String host, String baseUrl) {
      this(host, 80, baseUrl, null);
   }
   
   public UrlBuilder(String host, String baseUrl, String postfixUrl) {
      this(host, 80, baseUrl, postfixUrl);
   }
   
   public UrlBuilder(String host, Number port, String baseUrl,
         String postfixUrl) {
      this(Protocol.HTTP, host, port, baseUrl, postfixUrl);
   }
   
   public UrlBuilder(String host, Number port, String baseUrl) {
      this(Protocol.HTTP, host, port, baseUrl);
   }
   
   public UrlBuilder(Protocol protocol, String host, Number port,
         String baseUrl) {
      this(protocol, host, port, baseUrl, null);
   }
   
   public UrlBuilder(Protocol protocol, String host, Number port,
         String baseUrl, String postfixUrl) {
      setProtocol(protocol);
      setHost(host);
      setPort(port);
      setBaseUrl(baseUrl);
      setPostfixUrl(postfixUrl);
   }
   
   public static UrlBuilder createRelativeUrl(String baseUrl,
         String postfixUrl) {
      UrlBuilder builder = new UrlBuilder();
      builder.setBaseUrl(baseUrl);
      builder.setPostfixUrl(postfixUrl);
      return builder;
   }
   
   public <T> void addParameter(String name, T value) {
      getParameters().add(new NameValuePair<T>(name, value));
   }
   
   public <T> void addParameterChecked(String name, T value) {
      if(value != null) {
         getParameters().add(new NameValuePair<T>(name, value));
      }
   }
   
   public String getUrl() {
      return getURL();
   }
   
   public String getURL() {
      StringBuilder builder = new StringBuilder();
      
      //protocol
      if(getProtocol() != null) {
         builder.append(getProtocol().toString());
         builder.append("://");
      }
      
      //host
      if(getHost() != null) {
         builder.append(getHost());
      }
      
      //port
      if(getPort() == null || getPort() == 80) {
         //only add port 80 if specified to
         if(getAddPort80()) {
            builder.append(':').append(80);
         }
      }
      else if(getPort() != null) {
         builder.append(':').append(getPort());
      }
      
      //baseUrl
      if(getBaseUrl() != null) {
         builder.append(getBaseUrl());
      }
      
      //postfixUrl
      if(getPostfixUrl() != null) {
         builder.append(getPostfixUrl());
      }
      
      //parameters
      if(getParameters() != null || !getParameters().isEmpty()) {
         //check for '?'
         if(builder.charAt(builder.length() - 1) != '?') {
            builder.append('?');
         }
         for (NameValuePair<?> param : getParameters()) {
            builder.append(param.getName());
            builder.append('=');
            builder.append(param.getValue());
            builder.append('&');
         }
         builder.deleteCharAt(builder.length() - 1);
      }
      
      return builder.toString();
   }
   
   @Override
   public String toString() {
      return getUrl();
   }

   /**
    * @return the protocol
    */
   public Protocol getProtocol() {
      return _protocol;
   }

   /**
    * @param protocol the protocol to set
    */
   public void setProtocol(Protocol protocol) {
      _protocol = protocol;
   }

   /**
    * @return the host
    */
   public String getHost() {
      return _host;
   }

   /**
    * @param host the host to set
    */
   public void setHost(String host) {
      _host = host;
   }

   /**
    * @return the port
    */
   public Integer getPort() {
      return _port;
   }

   /**
    * @param port the port to set
    */
   public void setPort(Number port) {
      if(port == null) {
         _port = null;
      }
      _port = port.intValue();
   }

   /**
    * @param port the port to set
    */
   public void setPort(Integer port) {
      _port = port;
   }

   /**
    * @return the baseUrl
    */
   public String getBaseUrl() {
      return _baseUrl;
   }

   /**
    * @param baseUrl the baseUrl to set
    */
   public void setBaseUrl(String baseUrl) {
      _baseUrl = baseUrl;
   }

   /**
    * @return the postfixUrl
    */
   public String getPostfixUrl() {
      return _postfixUrl;
   }

   /**
    * @param postfixUrl the postfixUrl to set
    */
   public void setPostfixUrl(String postfixUrl) {
      _postfixUrl = postfixUrl;
   }

   /**
    * @return the defaultToPort80
    */
   public Boolean getAddPort80() {
      return _addPort80;
   }

   /**
    * @param defaultToPort80 the defaultToPort80 to set
    */
   public void setAddPort80(Boolean defaultToPort80) {
      _addPort80 = defaultToPort80;
   }
   
   /**
    * @return the parameters
    */
   public List<NameValuePair<?>> getParameters() {
      return _parameters;
   }

   /**
    * @param parameters the parameters to set
    */
   public void setParameters(List<NameValuePair<?>> parameters) {
      _parameters = parameters;
   }
   
   protected static class NameValuePair<T> {
      
      private String _name;
      
      private T _value;
      
      ////
      ////
      
      public NameValuePair() {
         //do nothing
      }
      
      public NameValuePair(String name, T value) {
         setName(name);
         setValue(value);
      }

      /**
       * @return the name
       */
      public String getName() {
         return _name;
      }

      /**
       * @param name the name to set
       */
      public void setName(String name) {
         _name = name;
      }

      /**
       * @return the value
       */
      public T getValue() {
         return _value;
      }

      /**
       * @param value the value to set
       */
      public void setValue(T value) {
         _value = value;
      }
   }

}
